<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mermaid图表转PNG工具</title>
    <script src="https://cdn.jsdelivr.net/npm/mermaid@10.9.1/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Microsoft YaHei', Arial, sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            max-width: 1400px;
            margin: 0 auto;
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 {
            text-align: center;
            color: #333;
            margin-bottom: 30px;
        }
        .editor-container {
            display: flex;
            gap: 20px;
            margin-bottom: 20px;
        }
        .editor-panel {
            flex: 1;
            min-height: 500px;
        }
        .editor-panel h3 {
            margin-top: 0;
            color: #555;
            border-bottom: 2px solid #007bff;
            padding-bottom: 10px;
        }
        textarea {
            width: 100%;
            height: 400px;
            padding: 15px;
            border: 2px solid #ddd;
            border-radius: 5px;
            font-family: 'Consolas', 'Monaco', monospace;
            font-size: 14px;
            line-height: 1.5;
            resize: vertical;
            box-sizing: border-box;
        }
        textarea:focus {
            outline: none;
            border-color: #007bff;
        }
        .preview-panel {
            flex: 1;
            min-height: 500px;
        }
        .preview-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
        }
        .preview-header h3 {
            margin: 0;
        }
        .mermaid {
            text-align: center;
            width: 100% !important;
            min-width: 100% !important;
            max-width: 100% !important;
            border: 2px solid #ddd;
            border-radius: 5px;
            padding: 20px;
            background: white;
            overflow: hidden;
            position: relative;
            box-sizing: border-box;
            flex-shrink: 0;
            flex-grow: 0;
            display: block;
        }
        .controls {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            flex-wrap: wrap;
        }
        .btn {
            padding: 10px 20px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 14px;
            font-weight: bold;
            transition: all 0.3s;
        }
        .btn-primary {
            background-color: #007bff;
            color: white;
        }
        .btn-primary:hover {
            background-color: #0056b3;
        }
        .btn-success {
            background-color: #28a745;
            color: white;
        }
        .btn-success:hover {
            background-color: #1e7e34;
        }
        .btn-warning {
            background-color: #ffc107;
            color: #212529;
        }
        .btn-warning:hover {
            background-color: #e0a800;
        }
        .btn-info {
            background-color: #17a2b8;
            color: white;
        }
        .btn-info:hover {
            background-color: #138496;
        }
        .settings {
            background: #f8f9fa;
            padding: 15px;
            border-radius: 5px;
            margin-bottom: 20px;
        }
        .settings h4 {
            margin-top: 0;
            color: #555;
        }
        .setting-group {
            display: flex;
            gap: 15px;
            align-items: center;
            margin-bottom: 10px;
            flex-wrap: wrap;
        }
        .setting-group label {
            font-weight: bold;
            min-width: 80px;
        }
        .setting-group input, .setting-group select {
            padding: 5px 10px;
            border: 1px solid #ddd;
            border-radius: 3px;
        }
        .examples {
            margin-top: 20px;
        }
        .example-btn {
            margin: 5px;
            padding: 5px 10px;
            background: #e9ecef;
            border: 1px solid #ced4da;
            border-radius: 3px;
            cursor: pointer;
            font-size: 12px;
        }
        .example-btn:hover {
            background: #dee2e6;
        }
        .error {
            color: #dc3545;
            background: #f8d7da;
            border: 1px solid #f5c6cb;
            padding: 10px;
            border-radius: 5px;
            margin: 10px 0;
        }
        /* 图表预览缩放控制 */
        .preview-controls {
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 10px;
            padding: 8px;
            background: #f8f9fa;
            border-radius: 5px;
        }
        .preview-controls .range {
            width: 120px;
        }
        .btn-light {
            background: #e9ecef;
            color: #212529;
            padding: 5px 10px;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            font-size: 12px;
        }
        .btn-light:hover { 
            background: #dde2e6; 
        }
        .range {
            width: 120px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🎨 Mermaid图表转PNG工具</h1>
        
        <div class="settings">
            <h4>⚙️ 导出设置</h4>
            <div class="setting-group">
                <label>分辨率倍数:</label>
                <select id="scaleSelect">
                    <option value="2">2倍 (标准)</option>
                    <option value="3" selected>3倍 (高清)</option>
                    <option value="4">4倍 (超高清)</option>
                    <option value="5">5倍 (极高清)</option>
                </select>
            </div>
            <div class="setting-group">
                <label>主题:</label>
                <select id="themeSelect">
                    <option value="default" selected>默认</option>
                    <option value="dark">深色</option>
                    <option value="forest">森林</option>
                    <option value="neutral">中性</option>
                </select>
            </div>
            <div class="setting-group">
                <label>字体大小:</label>
                <input type="number" id="fontSize" value="16" min="10" max="24">
            </div>
            <div class="setting-group">
                <label>预览区域高度(px):</label>
                <input type="number" id="previewHeight" value="600" min="300" max="1000">
            </div>
            <div class="setting-group">
                <label>外边距(px):</label>
                <input type="number" id="exportPadding" value="40" min="0" max="400">
            </div>
        </div>

        <div class="controls">
            <button class="btn btn-primary" onclick="renderDiagram()">🔄 渲染图表</button>
            <button class="btn btn-success" onclick="exportAsPNG()">📸 导出PNG</button>
            <button class="btn btn-warning" onclick="exportAsSVG()">📄 导出SVG</button>
            <button class="btn btn-info" onclick="clearEditor()">🗑️ 清空编辑器</button>
        </div>

        <div class="editor-container">
            <div class="editor-panel">
                <h3>📝 Mermaid代码编辑器</h3>
                <textarea id="mermaidCode" placeholder="在这里输入Mermaid代码...">sequenceDiagram
    autonumber
    participant A as 用户
    participant B as 系统
    participant C as 数据库

    A->>B: 发送请求
    B->>C: 查询数据
    C-->>B: 返回结果
    B-->>A: 响应数据

    alt 成功
        A->>A: 处理数据
    else 失败
        A->>A: 显示错误
    end</textarea>
            </div>
            
            <div class="preview-panel">
                <div class="preview-header">
                    <h3>👁️ 图表预览</h3>
                    <div class="preview-controls">
                        <button class="btn btn-light" onclick="zoomOut()">－</button>
                        <input type="range" min="25" max="300" value="100" class="range" id="previewZoomRange" onchange="setPreviewZoom(this.value)" />
                        <button class="btn btn-light" onclick="zoomIn()">＋</button>
                        <button class="btn btn-light" onclick="resetZoom()">重置</button>
                    </div>
                </div>
                <div id="errorMsg"></div>
                <div class="mermaid" id="diagram"></div>
            </div>
        </div>

        <div class="examples">
            <h4>📚 示例图表</h4>
            <button class="example-btn" onclick="loadExample('sequence')">时序图</button>
            <button class="example-btn" onclick="loadExample('flowchart')">流程图</button>
            <button class="example-btn" onclick="loadExample('pie')">饼图</button>
            <button class="example-btn" onclick="loadExample('class')">类图</button>
            <button class="example-btn" onclick="loadExample('state')">状态图</button>
            <button class="example-btn" onclick="loadExample('journey')">用户旅程图</button>
            
        </div>
    </div>


    <script>
        // 示例图表数据
        const examples = {
            sequence: `sequenceDiagram
    autonumber
    participant 用户
    participant 前端
    participant 后端
    participant 数据库

    用户->>前端: 登录请求
    前端->>后端: 验证用户信息
    后端->>数据库: 查询用户数据
    数据库-->>后端: 返回用户信息
    后端-->>前端: 返回Token
    前端-->>用户: 登录成功`,

            flowchart: `flowchart TD
    A[开始] --> B{判断条件}
    B -->|是| C[执行操作A]
    B -->|否| D[执行操作B]
    C --> E[处理结果]
    D --> E
    E --> F[结束]`,

            

            pie: `pie title 市场份额分布
    "苹果" : 45
    "三星" : 25
    "华为" : 15
    "小米" : 10
    "其他" : 5`,

            gitgraph: `gitgraph
    commit id: "初始提交"
    branch develop
    checkout develop
    commit id: "开发功能A"
    commit id: "开发功能B"
    checkout main
    merge develop
    commit id: "发布v1.0"`,

            class: `classDiagram
    class 用户 {
        +String 用户名
        +String 密码
        +login()
        +logout()
    }
    class 管理员 {
        +String 权限
        +manageUsers()
    }
    用户 <|-- 管理员`,

            state: `stateDiagram-v2
    [*] --> 待机
    待机 --> 运行 : 启动
    运行 --> 暂停 : 暂停
    暂停 --> 运行 : 继续
    运行 --> 停止 : 停止
    停止 --> [*]`,

            journey: `journey
    title 用户购物体验
    section 浏览商品
      进入网站: 5: 用户
      搜索商品: 4: 用户
      查看详情: 5: 用户
    section 购买流程
      加入购物车: 4: 用户
      结算支付: 3: 用户
      确认订单: 5: 用户
    section 收货评价
      等待发货: 2: 用户
      确认收货: 4: 用户
      评价商品: 5: 用户`
        };

        // 初始化Mermaid
        function initMermaid() {
            mermaid.initialize({
                startOnLoad: false,
                theme: document.getElementById('themeSelect').value,
                themeVariables: {
                    fontSize: document.getElementById('fontSize').value + 'px',
                    fontFamily: 'Microsoft YaHei, Arial, sans-serif'
                }
            });
        }

        // 渲染图表
        function renderDiagram() {
            const code = document.getElementById('mermaidCode').value;
            const diagramDiv = document.getElementById('diagram');
            const errorDiv = document.getElementById('errorMsg');
            
            // 清空错误信息
            errorDiv.innerHTML = '';
            
            try {
                // 清空并重新渲染
                diagramDiv.innerHTML = '';
                initMermaid();
                
                mermaid.render('diagram-svg', code).then(({svg}) => {
                    diagramDiv.innerHTML = svg;
                }).catch((error) => {
                    showError('渲染错误: ' + error.message);
                });
            } catch (error) {
                showError('语法错误: ' + error.message);
            }
        }

        // 显示错误信息
        function showError(message) {
            const errorDiv = document.getElementById('errorMsg');
            errorDiv.innerHTML = `<div class="error">❌ ${message}</div>`;
        }

        // 导出PNG
        function exportAsPNG() {
            const svg = document.querySelector('#diagram svg');
            if (!svg) {
                showError('请先渲染图表');
                return;
            }

            console.log('开始导出PNG，SVG元素:', svg);

            const scale = parseInt(document.getElementById('scaleSelect').value);
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');

            // 先获取内容的紧致边界，避免大面积留白
            const bbox = getSvgTightBBox(svg);
            const padding = Math.max(0, parseInt(document.getElementById('exportPadding').value || '0'));
            // 如果无法获得bbox，则退回到内在尺寸
            const fallBackSize = getSvgIntrinsicSize(svg);
            
            // 确保有有效的尺寸
            let svgWidth, svgHeight;
            if (bbox && bbox.width > 0 && bbox.height > 0) {
                svgWidth = bbox.width + padding * 2;
                svgHeight = bbox.height + padding * 2;
            } else if (fallBackSize.width > 0 && fallBackSize.height > 0) {
                svgWidth = fallBackSize.width + padding * 2;
                svgHeight = fallBackSize.height + padding * 2;
            } else {
                // 最后的回退：使用SVG的当前显示尺寸
                const rect = svg.getBoundingClientRect();
                svgWidth = Math.max(400, rect.width + padding * 2);
                svgHeight = Math.max(300, rect.height + padding * 2);
            }
            
            // 检查尺寸是否合理，如果异常大则使用显示尺寸
            const maxReasonableWidth = 3000;
            const maxReasonableHeight = 2000;
            if (svgWidth > maxReasonableWidth || svgHeight > maxReasonableHeight) {
                console.log('检测到异常大的尺寸，使用显示尺寸');
                const rect = svg.getBoundingClientRect();
                svgWidth = Math.max(400, Math.min(maxReasonableWidth, rect.width + padding * 2));
                svgHeight = Math.max(300, Math.min(maxReasonableHeight, rect.height + padding * 2));
            }

            console.log('SVG尺寸:', { svgWidth, svgHeight, bbox, fallBackSize });

            // 设置画布尺寸（高分辨率），并重置任何已有变换
            canvas.width = Math.max(1, Math.round(svgWidth * scale));
            canvas.height = Math.max(1, Math.round(svgHeight * scale));
            console.log('画布尺寸:', { width: canvas.width, height: canvas.height });
            
            ctx.setTransform(1, 0, 0, 1, 0, 0);
            ctx.fillStyle = 'white';
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            try {
                // 克隆一份SVG，不应用预览的缩放和拖拽状态，导出原始尺寸
                const cloned = svg.cloneNode(true);
                
                // 移除任何变换，导出原始尺寸
                cloned.style.transform = 'none';
                cloned.style.transformOrigin = 'initial';
                cloned.style.width = 'auto';
                cloned.style.height = 'auto';
                
                // 确保使用像素值的明确宽高
                cloned.setAttribute('width', svgWidth);
                cloned.setAttribute('height', svgHeight);
                // 保持比例且从原点开始
                cloned.setAttribute('preserveAspectRatio', 'xMinYMin meet');
                
                // 使用紧致边界作为viewBox，消除多余留白
                if (bbox && bbox.width > 0 && bbox.height > 0) {
                    const x = bbox.x - padding;
                    const y = bbox.y - padding;
                    const w = bbox.width + padding * 2;
                    const h = bbox.height + padding * 2;
                    cloned.setAttribute('viewBox', `${x} ${y} ${w} ${h}`);
                } else {
                    // 如果没有有效的bbox，使用默认的viewBox
                    cloned.setAttribute('viewBox', `0 0 ${svgWidth} ${svgHeight}`);
                }

                const svgData = new XMLSerializer().serializeToString(cloned);
                console.log('SVG数据长度:', svgData.length);
                
                // 创建内联SVG数据URL
                const svgDataUrl = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgData);
                console.log('SVG数据URL长度:', svgDataUrl.length);
                
                const img = new Image();
                img.crossOrigin = 'anonymous'; // 设置跨域属性
                
                img.onload = function() {
                    console.log('图片加载成功，尺寸:', { width: img.width, height: img.height });
                    try {
                        // 提升导出清晰度
                        ctx.imageSmoothingEnabled = true;
                        ctx.imageSmoothingQuality = 'high';
                        // 直接绘制到目标像素尺寸，避免二次缩放/扭曲
                        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
                        
                        const dataUrl = canvas.toDataURL('image/png', 1.0);
                        console.log('生成的PNG数据URL长度:', dataUrl.length);
                        
                        if (dataUrl.length < 100) {
                            console.error('生成的PNG数据过小，可能导出失败');
                            showError('导出PNG失败：生成的数据过小');
                            return;
                        }
                        
                        const link = document.createElement('a');
                        link.download = `mermaid图表_${scale}x.png`;
                        link.href = dataUrl;
                        link.click();
                        console.log('PNG导出完成');
                    } catch (error) {
                        console.error('导出PNG失败:', error);
                        showError('导出PNG失败，请尝试导出SVG格式');
                    }
                };
                
                img.onerror = function(error) {
                    console.error('图片加载失败:', error);
                    showError('图片加载失败，请尝试导出SVG格式');
                };
                
                img.src = svgDataUrl;
                
            } catch (error) {
                console.error('SVG处理失败:', error);
                showError('SVG处理失败，请尝试导出SVG格式');
            }
        }

        // 计算SVG的内在尺寸（避免被页面布局挤压导致导出变窄）
        function getSvgIntrinsicSize(svgEl) {
            // 1) 直接读width/height属性（若为数字或带px）
            const attrWidth = svgEl.getAttribute('width');
            const attrHeight = svgEl.getAttribute('height');
            const parseSize = (v) => {
                if (!v) return NaN;
                const num = parseFloat(v);
                return isNaN(num) ? NaN : num;
            };

            let width = parseSize(attrWidth);
            let height = parseSize(attrHeight);

            // 2) 使用viewBox
            if ((isNaN(width) || isNaN(height)) && svgEl.viewBox && svgEl.viewBox.baseVal) {
                const vb = svgEl.viewBox.baseVal;
                if (vb && vb.width > 0 && vb.height > 0) {
                    width = isNaN(width) ? vb.width : width;
                    height = isNaN(height) ? vb.height : height;
                }
            }

            // 3) 回退到可见尺寸
            if (isNaN(width) || isNaN(height)) {
                const rect = svgEl.getBoundingClientRect();
                width = isNaN(width) ? rect.width : width;
                height = isNaN(height) ? rect.height : height;
            }

            // 4) 限制最大尺寸，避免异常大的值
            const maxWidth = 5000;
            const maxHeight = 5000;
            if (width > maxWidth) width = maxWidth;
            if (height > maxHeight) height = maxHeight;

            return { width, height };
        }

        // 获取SVG内容的紧致边界（剔除巨大留白），若失败返回null
        function getSvgTightBBox(svgEl) {
            try {
                const bbox = svgEl.getBBox();
                if (!bbox || !isFinite(bbox.width) || !isFinite(bbox.height) || bbox.width <= 0 || bbox.height <= 0) {
                    return null;
                }
                
                // 限制最大尺寸，避免异常大的值
                const maxWidth = 5000;
                const maxHeight = 5000;
                let width = bbox.width;
                let height = bbox.height;
                
                if (width > maxWidth) width = maxWidth;
                if (height > maxHeight) height = maxHeight;
                
                return { x: bbox.x, y: bbox.y, width: width, height: height };
            } catch (e) {
                return null;
            }
        }

        // 导出SVG
        function exportAsSVG() {
            const svg = document.querySelector('#diagram svg');
            if (!svg) {
                showError('请先渲染图表');
                return;
            }

            console.log('开始导出SVG，SVG元素:', svg);

            // 在导出SVG时也加入留白
            const bbox = getSvgTightBBox(svg);
            const padding = Math.max(0, parseInt(document.getElementById('exportPadding').value || '0'));
            const fallBackSize = getSvgIntrinsicSize(svg);

            const cloned = svg.cloneNode(true);
            
            // 移除任何变换，导出原始尺寸
            cloned.style.transform = 'none';
            cloned.style.transformOrigin = 'initial';
            cloned.style.width = 'auto';
            cloned.style.height = 'auto';
            
            // 确保有有效的尺寸
            let svgWidth, svgHeight;
            if (bbox && bbox.width > 0 && bbox.height > 0) {
                svgWidth = bbox.width + padding * 2;
                svgHeight = bbox.height + padding * 2;
                const x = bbox.x - padding;
                const y = bbox.y - padding;
                cloned.setAttribute('viewBox', `${x} ${y} ${svgWidth} ${svgHeight}`);
                console.log('SVG导出尺寸:', { x, y, w: svgWidth, h: svgHeight });
            } else if (fallBackSize.width > 0 && fallBackSize.height > 0) {
                svgWidth = fallBackSize.width + padding * 2;
                svgHeight = fallBackSize.height + padding * 2;
                cloned.setAttribute('viewBox', `0 0 ${svgWidth} ${svgHeight}`);
                console.log('SVG导出尺寸(回退):', { w: svgWidth, h: svgHeight });
            } else {
                // 最后的回退：使用SVG的当前显示尺寸
                const rect = svg.getBoundingClientRect();
                svgWidth = Math.max(400, rect.width + padding * 2);
                svgHeight = Math.max(300, rect.height + padding * 2);
                cloned.setAttribute('viewBox', `0 0 ${svgWidth} ${svgHeight}`);
                console.log('SVG导出尺寸(最终回退):', { w: svgWidth, h: svgHeight });
            }
            
            // 检查尺寸是否合理，如果异常大则使用显示尺寸
            const maxReasonableWidth = 3000;
            const maxReasonableHeight = 2000;
            if (svgWidth > maxReasonableWidth || svgHeight > maxReasonableHeight) {
                console.log('检测到异常大的尺寸，使用显示尺寸');
                const rect = svg.getBoundingClientRect();
                svgWidth = Math.max(400, Math.min(maxReasonableWidth, rect.width + padding * 2));
                svgHeight = Math.max(300, Math.min(maxReasonableHeight, rect.height + padding * 2));
                cloned.setAttribute('viewBox', `0 0 ${svgWidth} ${svgHeight}`);
            }
            
            cloned.setAttribute('width', svgWidth);
            cloned.setAttribute('height', svgHeight);
            cloned.setAttribute('preserveAspectRatio', 'xMinYMin meet');

            const svgData = new XMLSerializer().serializeToString(cloned);
            console.log('SVG数据长度:', svgData.length);
            
            if (svgData.length < 100) {
                console.error('生成的SVG数据过小，可能导出失败');
                showError('导出SVG失败：生成的数据过小');
                return;
            }
            
            const svgBlob = new Blob([svgData], {type: 'image/svg+xml;charset=utf-8'});
            const svgUrl = URL.createObjectURL(svgBlob);

            const link = document.createElement('a');
            link.download = 'mermaid图表.svg';
            link.href = svgUrl;
            link.click();

            console.log('SVG导出完成');
            URL.revokeObjectURL(svgUrl);
        }

        // 清空编辑器
        function clearEditor() {
            document.getElementById('mermaidCode').value = '';
            document.getElementById('diagram').innerHTML = '';
            document.getElementById('errorMsg').innerHTML = '';
        }

        // 加载示例
        function loadExample(type) {
            document.getElementById('mermaidCode').value = examples[type];
            renderDiagram();
        }

        // 图表预览缩放功能
        let previewZoom = 100;

        function setPreviewZoom(percent) {
            previewZoom = Math.min(300, Math.max(25, parseInt(percent)));
            const diagram = document.getElementById('diagram');
            const svg = diagram.querySelector('svg');
            if (svg) {
                // 使用transform缩放和拖拽，不改变SVG的实际尺寸
                svg.style.transform = `scale(${previewZoom / 100}) translate(${svgOffsetX}px, ${svgOffsetY}px)`;
                svg.style.transformOrigin = 'top left';
                
                // 从配置中读取预览区域高度
                const previewHeight = document.getElementById('previewHeight').value || '600';
                
                // 强制保持diagram容器的固定尺寸，使用!important确保优先级
                diagram.style.setProperty('width', '100%', 'important');
                diagram.style.setProperty('height', previewHeight + 'px', 'important');
                diagram.style.setProperty('min-width', '100%', 'important');
                diagram.style.setProperty('max-width', '100%', 'important');
                diagram.style.setProperty('min-height', previewHeight + 'px', 'important');
                diagram.style.setProperty('max-height', previewHeight + 'px', 'important');
                diagram.style.overflow = 'hidden'; // 隐藏滚动条
                diagram.style.boxSizing = 'border-box';
                diagram.style.position = 'relative';
                diagram.style.flexShrink = '0';
                diagram.style.flexGrow = '0';
                diagram.style.display = 'block';
                
                // 重置SVG的宽高为原始尺寸，让transform生效
                svg.style.width = 'auto';
                svg.style.height = 'auto';
                
                // 确保SVG不会影响父容器的大小
                svg.style.maxWidth = 'none';
                svg.style.maxHeight = 'none';
                svg.style.position = 'absolute';
                svg.style.top = '0';
                svg.style.left = '0';
                
                // 不再自动重置拖拽偏移量，让用户可以自由拖拽
            }
        }

        function zoomIn() {
            setPreviewZoom(previewZoom + 25);
            document.getElementById('previewZoomRange').value = previewZoom;
        }

        function zoomOut() {
            setPreviewZoom(previewZoom - 25);
            document.getElementById('previewZoomRange').value = previewZoom;
        }

        function resetZoom() {
            // 重置缩放和拖拽位置
            svgOffsetX = 0;
            svgOffsetY = 0;
            setPreviewZoom(100);
            document.getElementById('previewZoomRange').value = 100;
        }


        // 拖拽相关变量
        let isDragging = false;
        let dragStartX = 0;
        let dragStartY = 0;
        let svgOffsetX = 0;
        let svgOffsetY = 0;

        // 为diagram容器添加鼠标滚轮缩放和拖拽功能
        function addWheelZoom() {
            const diagram = document.getElementById('diagram');
            if (diagram) {
                // 滚轮缩放
                diagram.addEventListener('wheel', function(e) {
                    e.preventDefault(); // 阻止默认滚动行为
                    
                    // 根据滚轮方向调整缩放
                    const delta = e.deltaY > 0 ? -10 : 10;
                    const newZoom = previewZoom + delta;
                    
                    // 限制缩放范围
                    if (newZoom >= 25 && newZoom <= 300) {
                        setPreviewZoom(newZoom);
                        document.getElementById('previewZoomRange').value = previewZoom;
                    }
                }, { passive: false });

                // 拖拽功能
                diagram.addEventListener('mousedown', function(e) {
                    const svg = diagram.querySelector('svg');
                    if (svg) { // 所有情况下都允许拖拽
                        isDragging = true;
                        dragStartX = e.clientX - svgOffsetX;
                        dragStartY = e.clientY - svgOffsetY;
                        diagram.style.cursor = 'grabbing';
                        e.preventDefault();
                    }
                });

                diagram.addEventListener('mousemove', function(e) {
                    if (isDragging) {
                        svgOffsetX = e.clientX - dragStartX;
                        svgOffsetY = e.clientY - dragStartY;
                        updateSvgPosition();
                    }
                });

                diagram.addEventListener('mouseup', function() {
                    if (isDragging) {
                        isDragging = false;
                        diagram.style.cursor = 'grab';
                    }
                });

                diagram.addEventListener('mouseleave', function() {
                    if (isDragging) {
                        isDragging = false;
                        diagram.style.cursor = 'grab';
                    }
                });

                // 设置鼠标样式
                diagram.style.cursor = 'grab';
            }
        }

        // 更新SVG位置
        function updateSvgPosition() {
            const diagram = document.getElementById('diagram');
            const svg = diagram.querySelector('svg');
            if (svg) {
                svg.style.transform = `scale(${previewZoom / 100}) translate(${svgOffsetX}px, ${svgOffsetY}px)`;
                svg.style.transformOrigin = 'top left';
            }
        }

        // 监听设置变化
        document.getElementById('themeSelect').addEventListener('change', renderDiagram);
        document.getElementById('fontSize').addEventListener('change', renderDiagram);
        document.getElementById('previewHeight').addEventListener('change', function() {
            // 当预览高度改变时，重新设置容器大小
            setPreviewZoom(previewZoom);
        });

        // 页面加载完成后自动渲染和添加滚轮缩放
        window.addEventListener('load', function() {
            renderDiagram();
            addWheelZoom();
        });
    </script>
</body>
</html>
